﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using AutoMapper;
using CollegeSim.Domain.DTO;
using CollegeSim.Domain.Interfaces.Repositories;

namespace CollegeSim.Persistence.Repositories
{
    // Author: Ryan Redburn
    // Project: CollegeSim
    // Date: 2/5/2014

    #region [ Updates ]

    // Author: Ryan Redburn
    // Date: 5/6/2014
    // Revisions: Changed repository to reflect version 2.0 changes. No major revisions.

    // Author: Ryan Redburn
    // Date: 5/20/2014
    // Revisions: Added exception handling for database connection problems, additional commenting, and missing method argument validation.

    #endregion

    /// <summary>
    /// Repository used to interact with student objects. Provides CRUD and search functionality,
    /// </summary>
    public class StudentRepository : IStudentRepository
    {
        #region [ Methods ]

        /// <summary>
        /// Returns all Students.
        /// </summary>
        /// <returns>IEnumerable of StudentDto.</returns>
        public IEnumerable<StudentDTO> FindAll()
        {
            IEnumerable<StudentDTO> students = null;

            // Open entity connection
            using (var context = new CollegeSimEntities())
            {
                try
                {
                    // Map retrieved dbset to targeted return type
                    students = Mapper.Map<IEnumerable<StudentDTO>>(context.Students);
                }
                catch (AutoMapperMappingException ex)
                {
                    // Check for database connection exception
                    if (ex.InnerException.InnerException is SqlException)
                    {
                        throw new InvalidOperationException(
                            "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                    }
                }
            }

            return students;
        }

        /// <summary>
        /// Returns all Students with the given name.
        /// </summary>
        /// <param name="firstName">Student first name.</param>
        /// <param name="lastName">Student last name.</param>
        /// <returns>IEnumerable of StudentDto.</returns>
        public IEnumerable<StudentDTO> FindByName(string firstName, string lastName)
        {
            // Confirm valid name values
            if (!String.IsNullOrWhiteSpace(firstName) && !String.IsNullOrWhiteSpace(lastName))
            {
                IEnumerable<StudentDTO> students = null;

                // Open entity connection
                using (var context = new CollegeSimEntities())
                {
                    try
                    {
                        // Map retrieved dbset to targeted return type
                        students = Mapper.Map<IEnumerable<StudentDTO>>(context.Students
                            .Where(s => s.Person.FirstName == firstName && s.Person.LastName == lastName));
                    }
                    catch (AutoMapperMappingException ex)
                    {
                        // Check for database connection exception
                        if (ex.InnerException.InnerException is SqlException)
                        {
                            throw new InvalidOperationException(
                                "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                        }
                    }
                }

                return students;
            }

            // Throw exception for invalid firstName value
            if (String.IsNullOrWhiteSpace(firstName))
            {
                throw new ArgumentNullException("firstName",
                    "firstName does not accept a null or empty string as an argument.");
            }

            // Throw exception for invalid lastName value
            if (String.IsNullOrWhiteSpace(lastName))
            {
                throw new ArgumentNullException("lastName",
                    "lastName does not accept a null or empty string as an argument.");
            }

            return null;
        }

        /// <summary>
        /// Returns a Student with the given id.
        /// </summary>
        /// <param name="studentID">Student integer id.</param>
        /// <returns>IEnumerable of StudentDto.</returns>
        public StudentDTO FindByID(int studentID)
        {
            // Throw exception on invalid instructorID value
            if (studentID < 0)
                throw new ArgumentOutOfRangeException("studentID",
                    "studentID cannot be less than zero.");

            StudentDTO student = null;

            // Open entity connection
            using (var context = new CollegeSimEntities())
            {
                try
                {
                    // Map retrieved dbset to targeted return type
                    student = Mapper.Map<StudentDTO>(context.Students.Single(s => s.StudentID == studentID));
                }
                catch (AutoMapperMappingException ex)
                {
                    // Check for database connection exception
                    if (ex.InnerException.InnerException is SqlException)
                    {
                        throw new InvalidOperationException(
                            "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                    }
                }
            }

            return student;
        }

        /// <summary>
        /// Saves a new Student.
        /// </summary>
        /// <param name="newStudent">New Student to save.</param>
        public void Insert(StudentDTO newStudent)
        {
            // Throw exception for null dto
            if (newStudent == null)
                throw new ArgumentNullException("newStudent",
                    "newStudent does not accept a null dto as an argument.");

            // Open entity connection
            using (var context = new CollegeSimEntities())
            {
                try
                {
                    // Add student and commit changes
                    context.Students.Add(Mapper.Map<Student>(newStudent));
                    context.SaveChanges();
                }
                catch (AutoMapperMappingException ex)
                {
                    // Check for database connection exception
                    if (ex.InnerException.InnerException is SqlException)
                    {
                        throw new InvalidOperationException(
                            "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                    }
                }
            }
        }

        /// <summary>
        /// Updates an existing Student.
        /// </summary>
        /// <param name="studentUpdate">Student to update.</param>
        public void Update(StudentDTO studentUpdate)
        {
            // Throw exception for null dto
            if (studentUpdate == null)
                throw new ArgumentNullException("studentUpdate",
                    "studentUpdate does not accept a null dto as an argument.");

            // Open entity connection
            using (var context = new CollegeSimEntities())
            {
                try
                {
                    // Confirm student does exist
                    var student = context.Students.SingleOrDefault(s => s.StudentID == studentUpdate.StudentID);

                    // Throws an exception if no matching entry is found
                    if (student == null)
                        throw new InvalidOperationException("No entry matching the given student was found.");

                    // Update student and commit changes
                    student.Code = studentUpdate.Code;
                    student.EnrollmentDate = studentUpdate.EnrollmentDate;
                    student.Person.FirstName = studentUpdate.FirstName;
                    student.Person.LastName = studentUpdate.LastName;
                    student.Person.PhoneNumber = studentUpdate.PhoneNumber;
                    student.Person.Email = studentUpdate.Email;

                    context.SaveChanges();
                }
                catch (AutoMapperMappingException ex)
                {
                    // Check for database connection exception
                    if (ex.InnerException.InnerException is SqlException)
                    {
                        throw new InvalidOperationException(
                            "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                    }
                }
            }
        }

        /// <summary>
        /// Deletes an existing Student.
        /// </summary>
        /// <param name="studentDel">Student to delete.</param>
        public void Delete(StudentDTO studentDel)
        {
            // Throw exception for null dto
            if (studentDel == null)
                throw new ArgumentNullException("studentDel",
                    "studentDel does not accept a null dto as an argument.");

            // Open entity connection
            using (var context = new CollegeSimEntities())
            {
                try
                {
                    // Confirm student does exist
                    var student = context.Students.SingleOrDefault(s => s.StudentID == studentDel.StudentID);

                    // Throws an exception if no matching entry is found
                    if (student == null)
                        throw new InvalidOperationException("No entry matching the given student was found.");

                    // Delete student and commit changes
                    context.Students.Remove(student);
                    context.SaveChanges();
                }
                catch (AutoMapperMappingException ex)
                {
                    // Check for database connection exception
                    if (ex.InnerException.InnerException is SqlException)
                    {
                        throw new InvalidOperationException(
                            "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                    }
                }
            }
        }

        #endregion
    }
}
